using System;
using UnityEngine;
using UnityEngine.Serialization;

public class MovementComponent : MonoBehaviour
{

    private enum EDirectionDetected
    {
        Top,
        Down,
        Left,
        Right,
        Forward,
        Backward
    }
    
    private Rigidbody rigidbody;
    private CapsuleCollider capsuleCollider;

    [SerializeField] private Transform rootCheck;
    [SerializeField] private Transform wallCheck;
    [SerializeField] private LayerMask groundLayer;
    [SerializeField] private LayerMask wallLayer;
    [SerializeField] private float moveSpeed = 3f;
    [SerializeField] private float jumpSpeed = 8f;
    [SerializeField] private float dashSpeed = 12f;
    [SerializeField] private float wallSlideSpeed = 1.0f;
    
    
    // 坡度
    [SerializeField] private float slope = 35.0f;
    
    [SerializeField] private bool showGizmos = false;

    private RaycastHit[] m_GroundHitResult = new RaycastHit[1];
    private RaycastHit[] m_DirectionHitResult = new RaycastHit[5];

    // 移动方向检测
    private RaycastHit[] m_moveDetectedHitResult = new RaycastHit[5];
    // 胶囊体半径
    private float capsuleRadius = 0.0f;
    [SerializeField] private float wallDistance = 0.1f;
    [SerializeField] private float groundDistance = 0.1f;

    private Player player;

    private void Awake()
    {
        rigidbody = GetComponent<Rigidbody>();
    }

    void Start()
    {
        
        capsuleCollider = GetComponent<CapsuleCollider>();
        capsuleRadius = capsuleCollider.radius;

        player = GetComponent<Player>();
    }

    public void AddForce(Vector3 force, ForceMode mode)
    {
        rigidbody.AddForce(force, mode);
    }

    public void AddMovement(Vector3 direction)
    {
        if (direction.x > 0)
        {
            if (AngleDetected(EDirectionDetected.Right, capsuleRadius + wallDistance, wallLayer, Vector3.zero) > slope)
                direction.x = 0;
        } 
        else if (direction.x < 0)
        {
            if (AngleDetected(EDirectionDetected.Left, capsuleRadius + wallDistance, wallLayer, Vector3.zero) > slope)
                direction.x = 0;
        }
        
        if (direction.z > 0)
        {
            if (AngleDetected(EDirectionDetected.Forward, capsuleRadius + wallDistance, wallLayer, Vector3.zero) > slope)
                direction.z = 0;
        }
        else if(direction.z < 0)
        {
            if (AngleDetected(EDirectionDetected.Backward, capsuleRadius + wallDistance, wallLayer, Vector3.zero) > slope)
                direction.z = 0;
        }
        
        if (direction.sqrMagnitude != 0)
        {
            var angle = AngleDetected(EDirectionDetected.Down, groundDistance, wallLayer, Vector3.zero);
            if (angle > 1f && angle < slope)
            {
                var normal = m_moveDetectedHitResult[0].normal;
                direction = Vector3.ProjectOnPlane(direction, normal);
            }
        }
        
        direction.Normalize();
        var velocity = rigidbody.linearVelocity;
        rigidbody.linearVelocity = new Vector3(direction.x * moveSpeed, velocity.y, direction.z * moveSpeed);
    }

    // 垂直，攀爬移动
    public void Climbing(float direction)
    {
        var velocity = rigidbody.linearVelocity;
        velocity.y = direction * wallSlideSpeed;
        rigidbody.linearVelocity = velocity;
    }
    
    public void SetGravity(bool enable)
    {
        rigidbody.useGravity = enable;
    }

    public Vector3 GetVelocity()
    {
        return rigidbody.linearVelocity;
    }

    public bool IsGround()
    {
        return DirectionDetected(EDirectionDetected.Down, groundDistance, groundLayer, Vector3.zero) != -1;
    }

    public bool IsWall()
    {
        return Physics.Raycast(wallCheck.position, player.controller.forward, 0.2f, wallLayer);
    }
    
    public bool IsAir()
    {
        return !IsGround() && rigidbody.linearVelocity.y < 0;
    }
    
    public void Jump()
    {
        var velocity = rigidbody.linearVelocity;
        velocity.y = jumpSpeed;
        rigidbody.linearVelocity = velocity;
    }

    public void Dash()
    {
        var direction = player.controller.forward;
        var angle = AngleDetected(EDirectionDetected.Down, groundDistance, wallLayer, Vector3.zero);
        Debug.Log("Dash Angle = " + angle);
        if (angle > 1f && angle < slope)
        {
            var normal = m_moveDetectedHitResult[0].normal;
            direction = Vector3.ProjectOnPlane(direction, normal);
        }

       
        Debug.Log("Dash direction = " + direction);
        rigidbody.linearVelocity = direction.normalized * dashSpeed;
    }

    // 方向检测，返回距离最近的索引
    private int DirectionDetected(EDirectionDetected dir, float distance, LayerMask layer, Vector3 offset)
    {
        var origin = rootCheck.position + offset;
        var direction = Vector3.zero;
        switch (dir)
        {
            case EDirectionDetected.Top:
                direction = Vector3.up * capsuleCollider.height;
                break;
            case EDirectionDetected.Down:
                direction = Vector3.down;
                break;
            case EDirectionDetected.Left:
                direction = Vector3.left;
                break;
            case EDirectionDetected.Right:
                direction = Vector3.right;
                break;
            case EDirectionDetected.Forward:
                direction = Vector3.forward;
                break;
            case EDirectionDetected.Backward:
                direction = Vector3.back;
                break;
        }

        var index = -1;
        var count = Physics.RaycastNonAlloc(origin, direction, m_moveDetectedHitResult, distance, layer);
        if (count > 0)
        {
            var minLength = float.MaxValue;
            for(var i = 0; i < count; i++)
            {
                var length = m_moveDetectedHitResult[i].distance;
                if (length < minLength)
                {
                    minLength = length;
                    index = i;
                }
            }
        }
        return index;
    }

    // 角度检测
    private float AngleDetected(EDirectionDetected dir, float distance, LayerMask layer, Vector3 offset)
    {
        var angle = 0f;
        var index = DirectionDetected(dir, distance, layer, offset);
        if (index >= 0)
        {
            Vector3 normal = m_moveDetectedHitResult[index].normal;
            angle = Vector3.Angle(Vector3.up, normal);
        }
        return angle;
    }

    private void OnDrawGizmos()
    {
        if (!showGizmos) return;
        
        var location = rootCheck.position;
        DrawDebug.Line(location, location + Vector3.right * (capsuleRadius + wallDistance), Color.red, wallLayer);
        DrawDebug.Line(location, location + Vector3.left * (capsuleRadius + wallDistance), Color.red, wallLayer);
        DrawDebug.Line(location, location + Vector3.forward * (capsuleRadius + wallDistance), Color.red, wallLayer);
        DrawDebug.Line(location, location + Vector3.back * (capsuleRadius + wallDistance), Color.red, wallLayer);
        DrawDebug.Line(location, location + Vector3.down * groundDistance, Color.red, wallLayer);

        location += new Vector3(0.1f, 0, 0);
        DrawDebug.Line(location, location + Vector3.down * groundDistance, Color.yellow, groundLayer);

        location = wallCheck.position;
        if (player)
        {
            DrawDebug.Line(location, location + (player.controller.forward * 0.2f), Color.red, wallLayer);
        }
        else
        {
            DrawDebug.Line(location, location + (transform.right * 0.2f), Color.yellow, wallLayer);
        }
        
    }
}
